from pygame.locals import * #@UnusedWildImport
from ShipClass import * #@UnusedWildImport
from math import ceil #@UnusedImport
from Graphics import Screen, Map #@Reimport
from Objects import *
import sys, random
import time
import pygame.event

diffBlockSize = 3000

def initializeMap():
    #Referencing the already created object master lists
    global enemyship_sprite, asteroid_sprite, starfield
    #Difficulty increases at increments of diffBlockSize
    numDiffBorders = math.ceil(starfield.mapSize[0] / diffBlockSize)
    
    #########  --  ASTEROIDS  --  ##########
    
    # for 0 - 7 in a mapSize of 12,000
    for i in range(0, numDiffBorders * 2):
        #Obtain random position
        randomPos = [int(i / 2) * diffBlockSize + random.randint(0, diffBlockSize), int(i / 2) * diffBlockSize + random.randint(0, diffBlockSize)]
        grid = (int(randomPos[0] / 500), int(randomPos[1] / 500))
        
        #If randomPos would be too close to other existing asteroids,
        #recalculate randomPos
        tooClose = True #We are assuming the worst with tooClose, initially
        while tooClose == True:
            for asteroids in asteroid_sprite:
                if asteroids.findAdjacency(grid):
                    tooClose = True
                    randomPos = [int(i / 2) * diffBlockSize + random.randint(0, diffBlockSize), int(i / 2) * diffBlockSize + random.randint(0, diffBlockSize)]
                    break
            tooClose = False
        
        temp = Asteroid(screen, randomPos)
        asteroid_sprite.add(temp)
        
    #########  --  TERRITORIES  --  ##########
    
    #For each territory, 64 in a mapSize of 12,000
    numTerrOneDim = int(starfield.mapSize[0] / starfield.terrSize)
    numGridPerTerr = int(math.pow(starfield.terrSize / 500, 2))
    for x in range(0, numTerrOneDim):
        for y in range(0, numTerrOneDim):
            #Determine how many grids should be populated,
            #taking difficulty increase into account
            for j in range(0, numDiffBorders):
                #Where diffBlockSize * j begins with the outermost areas
                #and moves inward with Decreasing difficulty
                if x * starfield.terrSize >= int((diffBlockSize * j) / 2) and x * starfield.terrSize < starfield.mapSize[0] - int((diffBlockSize * j) / 2):
                    if y * starfield.terrSize >= int((diffBlockSize * j) / 2) and y * starfield.terrSize < starfield.mapSize[1] - int((diffBlockSize * j) / 2):
                        #After we check both x and y dimensions to make sure
                        #they are within this specific difficulty border
                        #starfield.terr[x][y] = random.randint(5, 7) - (j * 2) - 1
                        #starfield.terr[x][y] = random.randint(5, 7) - int(math.pow(j, 2))
                        starfield.terr[x][y] = random.randint(5, 7) - int(math.pow(j, 2))
                        #Ensure territory population is valid,
                        #with respect to the number of grids per territory
                        if starfield.terr[x][y] > numGridPerTerr:
                            starfield.terr[x][y] = numGridPerTerr
                        #if starfield.terr[x][y] < 1:
                            #starfield.terr[x][y] = 1
                        if starfield.terr[x][y] < 0:
                            starfield.terr[x][y] = 0
                            
#    for x in range(0, numTerrOneDim):
#        for y in range(0, numTerrOneDim):
#            print(x, y, starfield.terr[x][y])

    #########  --  ENEMIES  --  ##########
    offset = [250, 250] #Center of grid
    for x in range(0, numTerrOneDim):
        for y in range(0, numTerrOneDim):
            #Pick random grids
            gridsPerTerrOneDim = int(math.sqrt(numGridPerTerr))
            gridOptions = [[(j, k) for j in range(0, gridsPerTerrOneDim)] for k in range(0, gridsPerTerrOneDim)]
            gridsToSpawn = []
            for i in range(0, starfield.terr[x][y]):
                #Construct list of random grids for spawn locations
                randGridx = random.randint(0, 2)
                randGridy = random.randint(0, 2)
                
                #Check to see if the grid has been picked yet
                if gridOptions[randGridx][randGridy] != None:
                    #Add to final list to spawn
                    gridsToSpawn.append((randGridx, randGridy))
                    #Remove from list of options
                    gridOptions[randGridx][randGridy] = None
            
#            print(x, y, gridsToSpawn.__len__())
#            for i in gridsToSpawn:
#                print(i)
            
            #Determine specific enemy locations from gridsToSpawn
            terrLoc = [starfield.terrSize * x, starfield.terrSize * y]
            for k in gridsToSpawn:
                #Spawn the enemies
                
                #One in the center
                actualLoc = [terrLoc[0] + k[0] * 500 + offset[0], terrLoc[1] + k[1] * 500 + offset[1]]
                temp = EnemyShip(screen, actualLoc)
                enemyship_sprite.add(temp)
                
                #One at the right edge
                actualLoc[0] = actualLoc[0] + offset[0]
                temp = EnemyShip(screen, actualLoc)
                enemyship_sprite.add(temp)
                
                #One at the left edge
                actualLoc[0] = actualLoc[0] - offset[0] * 2
                temp = EnemyShip(screen, actualLoc)
                enemyship_sprite.add(temp)
                

#Sprite Groups
playership_sprite = pygame.sprite.RenderClear()     #list containing the player ship

shield_sprite = pygame.sprite.RenderClear() #list containing shield

enemyship_sprite = pygame.sprite.RenderClear()      #list containing all enemy ships
active_enemyship_sprite = pygame.sprite.RenderClear()

pmissile_sprite = pygame.sprite.RenderClear()       #list containing all player missiles
active_pmissile_sprite = pygame.sprite.RenderClear()

emissile_sprite = pygame.sprite.RenderClear()       #list containing all enemy missiles
active_emissile_sprite = pygame.sprite.RenderClear()

asteroid_sprite = pygame.sprite.RenderClear()       #list containing all asteroids
active_asteroid_sprite = pygame.sprite.RenderClear()

powerUp_sprite = pygame.sprite.RenderClear()        #list containing all powerups
active_powerUp_sprite = pygame.sprite.RenderClear()

Black = (0, 0, 0)
screen = Screen()
deathCount = 0

#Limit framerate
clockFramerate = pygame.time.Clock()

timeOfDeath = 0
timeIntro = 0

#place holder for missile
missile = None

#Used to disable player control for a time
playerControl = True

starfield = Map(screen)

#baseCollide - Tracks the ship's collision with the asteroids on the map;
#          if a collision is found, it will not update until next cycle.
baseCollide = False

#Compute the center of screen (including the size of the ship) in order to spawn correctly
scr_width, scr_height = screen.window.get_size()

#Pass size of the map to Ship constructor. It will spawn ship to the middle of the map
centerOfMap = [starfield.mapSize[0] / 2, starfield.mapSize[1] / 2]
ship = Ship((scr_width, scr_height), centerOfMap)
playership_sprite.add(ship)

ship.update(starfield.mapSize)
ship.displayData(False)

#######TESTING POWER UPS########
powerUp_sprite.add(PowerUpSpeed(screen, [centerOfMap[0] + 100, centerOfMap[1] + 100]))
powerUp_sprite.add(PowerUpWeapon(screen, [centerOfMap[0] + 200, centerOfMap[1] + 200]))
powerUp_sprite.add(PowerUpShield(screen, [centerOfMap[0] + 300, centerOfMap[1] + 300]))

#Randomly generate map
initializeMap()

starfield.update(screen.window, ship.position)
screen.updateDisplay()
pygame.mouse.set_visible(False)

######  --  (NON-REPEATED) KEY PRESSES  --  #####
missileFired = False

while 1:
    if playerControl == True:
        key_pressed = pygame.key.get_pressed()
        if key_pressed[pygame.K_ESCAPE]:
            sys.exit()
            
        if key_pressed[pygame.K_LEFT]:
            """rotate ship counterclockwise"""
            ship.rotate(5)
            
        if key_pressed[pygame.K_RIGHT]:
            """rotate ship clockwise"""
            ship.rotate(-5)
            
        if key_pressed[pygame.K_UP]:
            """velocity + """
            ship.accelerate()
            ship.update(starfield.mapSize)
            
        if key_pressed[pygame.K_DOWN]:
            """velocity - """
            ship.decelerate()
            ship.update(starfield.mapSize) 
        
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()            
                   
            if event.type == KEYDOWN:
                key_pressed = pygame.key.get_pressed()

                if key_pressed[pygame.K_x]:
                    "Brake to a stop"
                    ship.speed = 0
                
                if key_pressed[pygame.K_k]:
                    timeOfDeath = pygame.time.get_ticks()
                    playerControl = False
                    ship.kill()
                
                if key_pressed[pygame.K_f]:
                    """shoot missile"""
                    if missileFired == False:
                        missileFired = True
                        ship.fire(screen, pmissile_sprite)
                    
            if event.type == KEYUP:                
                if key_pressed[pygame.K_f]:
                    missileFired = False
        
    #If playerControl is False
    if playerControl == False:
        
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
                
            #Press Space to respawn
            if event.key == pygame.K_SPACE:
                #Must wait at least a second
                if pygame.time.get_ticks() - timeOfDeath > 1000:
                    ship.respawn()
                    playership_sprite.add(ship)
                    #Return control to the player
                    playerControl = True
                    timeOfDeath = 0
            #Player may also just quit
            if event.key == pygame.K_ESCAPE:
                sys.exit()
    
    """Draw the new position with background covering the old location and updates the screen.""" 
    screen.window.fill(Black)
    
    enemy_player_collision = False
    pmissile_enemy_collision = False
    emissile_player_collision = False
    
    #Move the background behind the ship 
    starfield.update(screen.window, ship.position)
    
    #Iterate through master list and find ones that should be rendered
    for asteroids in asteroid_sprite:
        temp = asteroids.update(screen.window, ship.position)
        if temp == None:
            #Asteroid should not be rendered (is not active)
            asteroids.remove(active_asteroid_sprite)
        else:
            #It is active and should be rendered
            active_asteroid_sprite.add(temp)
        if baseCollide == False and playerControl == True:
            baseCollide = pygame.sprite.collide_rect(ship, asteroids)
            homeBase = asteroids
    #Draw all sprites in list to the screen.
    #Maybe this will be more efficient than using blit() for every asteroid.
    active_asteroid_sprite.draw(screen.window)
    

    for missile in pmissile_sprite:
        missile.update_position()
        temp = missile.update(screen.window, ship.position)
        if temp == None:
            #Missile should not be rendered (is not active)
            missile.remove(active_pmissile_sprite)
        else:
            #It is active and should be rendered
            active_pmissile_sprite.add(temp)
        active_pmissile_sprite.draw(screen.window)
    
    """Removes missile from active_pmissile_sprites"""
    for missile in active_pmissile_sprite:
        if missile.lifetime > 25:
            active_pmissile_sprite.remove(missile)
            pmissile_sprite.remove(missile)
        
    """Kill missile and enemy if they collide"""
    for missile in active_pmissile_sprite:
        for enemy in active_enemyship_sprite:
            pmissile_enemy_collision = pygame.sprite.collide_rect(enemy, missile)
            if pmissile_enemy_collision == True:
                missile.kill()
                enemy.kill()
                
    """Test for power up collision"""
    for powerUp in powerUp_sprite:
        powerUp_collision = pygame.sprite.collide_rect(ship, powerUp)
        if powerUp_collision == True:
            if powerUp.name == "shield":
                shield = Shield((scr_width, scr_height), ship.position)
                shield_sprite.add(shield)
                powerUp.kill()
            else:
                ship.grantPowerUp(powerUp, pygame.time.get_ticks(), playership_sprite)
                powerUp.set_picked_up(pygame.time.get_ticks())
            
    """Test for power up timeout"""
    ship.checkPowerUps(pygame.time.get_ticks())
        
    """Draw shield"""
    if(len(shield_sprite) > 0):
        shield_sprite.draw(screen.window)    
                
    for enemy in enemyship_sprite:
        #print("Enemy_Position:", enemy.position)
        temp = enemy.update(screen.window, ship.position)
        if temp == None:
            #Enemy should not be rendered (is not active)
            enemy.remove(active_enemyship_sprite)
        else:
            #It is active and should be rendered
            active_enemyship_sprite.add(temp)
            temp.spawn(screen.window, ship.position)  
    active_enemyship_sprite.draw(screen.window)
    
    """Kill player and enemy when they collide"""
    for enemy in enemyship_sprite:
        enemy_player_collision = pygame.sprite.collide_rect(enemy, ship)
        if enemy_player_collision == True:
            enemy.remove(active_enemyship_sprite)
            enemy.remove(enemyship_sprite)
            
            timeOfDeath = pygame.time.get_ticks()
            playerControl = False
            ship.kill()
    
    #Draw power ups
    for powerUp in powerUp_sprite:
        temp = powerUp.update(screen.window, ship.position)
        if temp != None:
            active_powerUp_sprite.add(temp)
    active_powerUp_sprite.draw(screen.window)
    
    #If we collided with an asteroid,
    #Change the spawn point to the asteroid
    if baseCollide == True:
        ship.changeSpawn(homeBase.position)
    
    
    font = pygame.font.Font(None, 20)
    
    timeIntro = pygame.time.get_ticks()
    
    if timeIntro < 3000:
        text = font.render("Space Control by James Stafford, Joseph Myers, Matthew Langston.", 1, (255, 255, 255))
        textpos = text.get_rect(centerx=screen.window.get_width() / 2, top=90)
        screen.window.blit(text, textpos)
    
    if timeIntro >= 3000 and timeIntro < 6000:
        text1 = font.render("Use the up key to increase velocity and the down key to decrease velocity.", 1, (255, 255, 255))
        textpos1 = text.get_rect(centerx=screen.window.get_width() / 2, top=90)
        screen.window.blit(text1, textpos1)
    
    if timeIntro >= 6000 and timeIntro < 9000:
        text2 = font.render("Use the right and left key to turn right and left and f to fire a missile.", 1, (255, 255, 255))
        textpos2 = text.get_rect(centerx=screen.window.get_width() / 2, top=90)
        screen.window.blit(text2, textpos2)
    
    if timeIntro >= 9000 and timeIntro < 12000:
        text3 = font.render("Avoid other ships and save at asteroids.", 1, (255, 255, 255))
        textpos3 = text.get_rect(centerx=screen.window.get_width() / 2, top=90)
        screen.window.blit(text3, textpos3)
        
    #This should only iterate once
    for ships in playership_sprite:
        ships.update(starfield.mapSize)
    playership_sprite.draw(screen.window)
    
    #Will eventually need to change this to something like
    #screen.updateActors() for more than just our own ship
    screen.window.blit(ship.image, ship.rect)
    #If the player has died
    if timeOfDeath != 0:
        font = pygame.font.Font(None, 30)
        text = font.render("You have died. Press Space to respawn!", 1, (255, 255, 255))
        textpos = text.get_rect(centerx=screen.window.get_width() / 2, top=90)
        screen.window.blit(text, textpos)
    
    #Limit framerate to 45 FPS
    clockFramerate.tick(45)
    
    #HUD and debug information
    ship.displayData(baseCollide)
    
    #Flip the entire screen, since we'll be changing the whole
    #background every time anyway.
    screen.updateDisplay()
    
    #Reset baseCollide
    baseCollide = False
    
